﻿#if WINDOWS
using System.Net.NetworkInformation;
#elif ANDROID
using Android.Runtime;
using Android.Widget;
using Java.Interop;
using Application = Android.App.Application;
#endif

using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using AnyTap.Models;
using Serilog;
namespace AnyTap.Services;

/// <summary>
/// 静态全局类
/// </summary>
public static class Helper
{
	#region 定义
	/// <summary>
	/// 工作文件夹名称
	/// </summary>
	public const string HOME_FOLDER = "AnyTap/";

	/// <summary>
	/// 系统设置文件夹名称
	/// </summary>
	public const string SYS_FOLDER = "Sys/";

	/// <summary>
	/// 测试档案文件夹名称
	/// </summary>
	public const string DOC_FOLDER = "Doc/";

	/// <summary>
	/// 扩展名-档案
	/// </summary>
	public const string EXT_DOC = ".tf";

	/// <summary>
	/// 扩展名-数据库
	/// </summary>
	public const string EXT_DB = ".db";

	/// <summary>
	/// 工作目录
	/// </summary>
	public static readonly string DIR_WORK = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "/" + HOME_FOLDER;

	/// <summary>
	/// 语言配置目录
	/// </summary>
	public static readonly string DIR_LANGUAGE = DIR_WORK + @"Language/";

	/// <summary>
	/// 测试数据文件夹
	/// </summary>
	public static readonly string DIR_DATA = DIR_WORK + @"Data/";

	/// <summary>
	/// 系统设置文件夹
	/// </summary>
	public static readonly string DIR_SYS = DIR_WORK + SYS_FOLDER;

	/// <summary>
	/// 档案文件夹
	/// </summary>
	public static readonly string DIR_DOC = DIR_WORK + DOC_FOLDER;

	/// <summary>
	/// 常规配置
	/// </summary>
	public static readonly string FILE_SETTING = DIR_SYS + "Setting.json";

	/// <summary>
	/// 版本
	/// </summary>
	public static readonly string FILE_ASSEMBLY = DIR_DATA + "Assembly.json";

	/// <summary>
	/// 日志
	/// </summary>
	public static readonly string DIR_LOG = DIR_WORK + @"logs/";
	#endregion

	#region 属性
	public static Setting Settings { get; set; }

	/// <summary>
	/// 当前测试档案
	/// </summary>
	public static Doc Doc { get; set; }
	#endregion

	#region 函数
	#region 序列化
	/// <summary>
	/// 将T转换为byte数据,并添加到缓冲区
	/// </summary>
	/// <typeparam name="T">类型</typeparam>
	/// <param name="val">值</param>
	public static byte[] StructureToPtr<T>(T val) where T : struct
	{
		var size = Marshal.SizeOf(typeof(T));
		var pStruct = Marshal.AllocHGlobal(size);
		var array = new byte[size];
		try
		{
			Marshal.StructureToPtr(val, pStruct, true);
			Marshal.Copy(pStruct, array, 0, size);
		}
		finally
		{
			Marshal.FreeHGlobal(pStruct);
		}
		return array;
	}

	/// <summary>
	/// byte数组转换为T,返回转换的字节数
	/// </summary>
	public static int PtrToStructure<T>(byte[] rec, int index, out T val) where T : struct
	{
		var size = Marshal.SizeOf(typeof(T));
		var pStruct = Marshal.AllocHGlobal(size);
		try
		{
			Marshal.Copy(rec, index, pStruct, size);
			val = (T)Marshal.PtrToStructure(pStruct, typeof(T));
		}
		catch
		{
			val = default;
		}
		finally
		{
			Marshal.FreeHGlobal(pStruct);
		}
		return size;
	}

	private static readonly JsonSerializerOptions writeOptions = new() { IgnoreReadOnlyProperties = true, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping };

	/// <summary>
	/// 序列化
	/// </summary>
	/// <param name="val">值</param>
	/// <returns>Json字符串</returns>
	public static string WriteObject<T>(this T val) where T : class
	{
		try
		{
			return JsonSerializer.Serialize(val, writeOptions);
		}
		catch(Exception x)
		{
			Log.Fatal(x.Message);
		}
		return null;
	}

	/// <summary>
	/// 序列化
	/// </summary>
	public static void WriteObject<T>(this T val, string path) where T : class
	{
		try
		{
			File.WriteAllText(path, val.WriteObject(), Encoding.UTF8);
		}
		catch(Exception x)
		{
			Log.Fatal(x.Message);
		}
	}

	/// <summary>
	/// 反序列化
	/// </summary>
	public static T ReadObjectFromString<T>(string buffer) where T : class
	{
		try
		{
			return JsonSerializer.Deserialize<T>(buffer);
		}
		catch(Exception x)
		{
			Log.Fatal(x.Message);
		}

		return null;
	}

	/// <summary>
	/// 反序列化
	/// </summary>
	public static T ReadObject<T>(string path) where T : class
	{
		if(File.Exists(path))
		{
			var val = ReadObjectFromString<T>(File.ReadAllText(path));
			if(val is null)
			{
				Console.WriteLine($"ReadObject Exception:{path}");
			}
			return val;
		}
		return null;
	}

	/// <summary>
	/// 深拷贝
	/// </summary>
	/// <param name="source">值</param>
	public static T Clone<T>(this T source)
	{
		try
		{
			var type = source.GetType();
			return (T)JsonSerializer.Deserialize(JsonSerializer.Serialize(source, type, writeOptions), type);
		}
		catch(Exception x)
		{
			Log.Error(x.Message);
		}
		return default;
	}
	#endregion

	#region Device
	/// <summary>
	/// 设备ID
	/// </summary>
	public static string GetDeviceID()
	{
#if WINDOWS
		return NetworkInterface.GetAllNetworkInterfaces()
			.Where(nic =>
				nic.NetworkInterfaceType is NetworkInterfaceType.Ethernet or NetworkInterfaceType.Wireless80211)
			.Select(nic => nic.GetPhysicalAddress().ToString())
			.FirstOrDefault();
#elif ANDROID
		var id = GetSerialField();
		if(string.IsNullOrWhiteSpace(id) || id == Android.OS.Build.Unknown || id == "0")
		{
			try
			{
				id = Android.Provider.Settings.Secure.GetString(Android.App.Application.Context.ContentResolver, Android.Provider.Settings.Secure.AndroidId);
			}
			catch(Exception x)
			{
				id = Guid.NewGuid().ToString();
				Serilog.Log.Fatal(x.Message);
			}
		}

		static string GetSerialField()
		{
			JniPeerMembers buildMembers = new XAPeerMembers("android/os/Build", typeof(Android.OS.Build));
			try
			{
				const string id = "SERIAL.Ljava/lang/String;";
				var value = buildMembers.StaticFields.GetObjectValue(id);
				return JNIEnv.GetString(value.Handle, JniHandleOwnership.TransferLocalRef);
			}
			catch
			{
				return string.Empty;
			}
		}
		return id;
#elif true
		return UIKit.UIDevice.CurrentDevice.IdentifierForVendor.AsString();
#else
		return Guid.NewGuid().ToString();
#endif
	}
	#endregion

	#region 测试档案
	/// <summary>
	/// 保存测试档案
	/// </summary>
	public static void SaveDocFile() => SaveDocFile(Settings.LatestDocName);

	/// <summary>
	/// 保存测试档案
	/// </summary>
	public static void SaveDocFile(string name)
	{
		if(name is null || Doc is null)
			return;

		WriteObject(Doc, DIR_DOC + name + EXT_DOC);
	}

	/// <summary>
	/// 保存测试档案. 由于新建项doc为null,故不检查空值
	/// </summary>
	public static void SaveDocFile(string name, Doc doc) => WriteObject(doc, DIR_DOC + name + EXT_DOC);

	/// <summary>
	/// 删除测试档案
	/// </summary>
	public static void DelDocFile(string name)
	{
		if(name == null)
			return;

		File.Delete(DIR_DOC + name + EXT_DOC); //删除档案文件
		Array.ForEach(Directory.GetFiles(DIR_DATA, name + ".*"), s => File.Delete(s)); //删除测试数据
	}

	/// <summary>
	/// 加载指定档案
	/// </summary>
	/// <param name="name"></param>
	/// <returns></returns>
	public static void LoadDocFile(string name)
	{
		Settings.LatestDocName = name;
		if(name == null)
		{
			Doc = null;
		}
		else
		{
			var file = DIR_DOC + Settings.LatestDocName + EXT_DOC;
			var info = new FileInfo(file);
			Doc = ReadObject<Doc>(file);
			if(Doc == null)
			{
				Doc = new Doc();
			}
			else
			{
				info.LastAccessTime = DateTime.Now;
			}
			Doc.Items ??= [];
		}
		Settings.WriteObject(FILE_SETTING);
	}

	/// <summary>
	/// 档案文件是否存在
	/// </summary>
	public static bool IsDocExists(string name) => File.Exists(DIR_DOC + name + EXT_DOC);
	#endregion

	/// <summary>
	/// 显示简短消息
	/// </summary>
#if ANDROID //使用Snackbar替代Toast,临时解决频繁切换界面崩溃问题
	public static void ShowToast(string message) => Toast.MakeText(Application.Context, message, ToastLength.Long).Show();
#else
	public static void ShowToast(string message) => CommunityToolkit.Maui.Alerts.Toast.Make(message, CommunityToolkit.Maui.Core.ToastDuration.Short, 24).Show();
#endif
	#endregion

	#region 公共类型
	/// <summary>
	/// 公共状态代码
	/// </summary>
	public enum Code
	{
		/// <summary>
		/// 设备正常
		/// </summary>
		None,

		/// <summary>
		/// 设备离线
		/// </summary>
		Offline,

		/// <summary>
		/// 未发现设备
		/// </summary>
		NotFound,
	}

	public interface IRemark
	{
		public string Remark { get; }
	}

	public static string GetEnumRemark<T>(Enum item) where T : IRemark
	{
		var type = item.GetType();
		var label = item.ToString();
		var field = type.GetField(label);
		if(field.IsDefined(typeof(T), true) && field.GetCustomAttribute(typeof(T), true) is T attr)
			return attr.Remark;
		return label;
	}

	[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
	public class DetailRemarkAttribute(string remark) : Attribute, IRemark
	{
		public string Remark { get; } = remark;
	}

	/// <summary>
	/// Remark特性
	/// </summary>
	[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
	public class LabelRemarkAttribute(string remark) : Attribute, IRemark
	{
		public string Remark { get; } = remark;

		public static List<string> GetEnumLabels(Type type)
		{
			var labels = new List<string>();
			foreach(var item in Enum.GetValues(type))
			{
				var label = item.ToString();
				var field = type.GetField(label);
				if(field.IsDefined(typeof(LabelRemarkAttribute), true) && field.GetCustomAttribute(typeof(LabelRemarkAttribute), true) is LabelRemarkAttribute attr)
				{
					labels.Add(attr.Remark);
				}
				else
				{
					labels.Add(label);
				}
			}
			return labels;
		}
	}
	#endregion
}
